package sweet.toolbox.command

import sweet.helper.{ PropsContainer, Logger }

/** A container for Commands objects */
trait CommandContainer {
  private val _commands = new scala.collection.mutable.HashMap[String, Command]
  def commands = _commands.readOnly

  def addCommands(tuples: (String, Command)*){
    for((k,v) <- tuples) _commands(k) = v  
  }
}

/** A configuration object for Toolbox.*/
trait ToolboxConfig extends CommandContainer with PropsContainer{
  addSysProp("sweet.toolbox.home", "./")
  
  //load config properties file
  loadSysProps("sweet.toolbox.config", "classpath:config.properties")
}


/** A command trait can be executed and has a summary line and block of usage text. */
trait Command {
  def summary: String
  def usage: String
  def exec(args: Array[String]): Unit
}

/** Base trait for a subclass to extends functionalities. One important method it 
 * provide is withEachTemplate, it will loop through all template file found and
 * call a provide closure to further processing.
 */
trait ToolboxCommand extends Command with Logger {
  import freemarker.template._
  import java.io._
  import sweet.helper._  
  import sweet.helper.fm._
  
  import StringHelper.string2helper // implicit string enhancer
  import FileHelper.file2helper // implicit java File enhancer
  import Regex.pattern2regex // implicit java File enhancer

  def toolboxProps: scala.collection.Map[String, String]
  
  val toolboxProjectFilename = ".toolboxProject"
  
  val filenameREPrefix = "R_"
  val filenameRESuffix = "_R"
  val filenameREEscape = filenameREPrefix
    
  def createFreemarkerConfig(templateDir: File): Configuration = {
    val fmConfig = new ScalaFreemarkerConfiguration
    logger.debug("Loading template from directory " +templateDir.getAbsolutePath) 
    fmConfig.setDirectoryForTemplateLoading(templateDir)
    
    fmConfig
  }
  
  lazy val excludeDirRE = Regex(toolboxProps("project.excludeDirPattern"))
  def excludeDir(dir: File) = excludeDirRE.find(dir.getPathname)
  
  lazy val excludeFileRE = Regex(toolboxProps("project.excludeFilePattern"))
  def excludeFile(file: File) = excludeFileRE.find(file.getName)
    
  def withEachTemplate(templateDir: File, destDir: File)(func: (Template, String)=>Unit){
    val fmCfg = createFreemarkerConfig(templateDir)
    val templateDirPathname = templateDir.getPathname
    val templateDirPathnameLenPlusOne = templateDirPathname.length+1
    //For each file in templateDir, run it through FM engine and re-create it on destDir
    templateDir.eachDir{ dir =>
      if(!excludeDir(dir)){
        val files = dir.listSortedFiles
        var count = 0
        for( ftl <- files){
          count +=1
          
          if(ftl.isFile && !excludeFile(ftl)){        
            val ftlPathname = ftl.getPathname.substring(templateDirPathnameLenPlusOne)
            logger.debug("Reading template file "+ftlPathname)            
            val template = fmCfg.getTemplate(ftlPathname)
            func(template, ftlPathname)
          }
        }
        
        //If there is no file to processed, copy the empty directory.
        if(count ==0){
          val destDirName = dir.getPathname
          logger.debug("Creating directory " + destDirName)
          new File(destDir, destDirName).mkdirs()
        } 
      }
    }
  }
  
  def withTemplate(ftl: File)(func: (Template, String)=>Unit){
    val fmCfg = createFreemarkerConfig(ftl.getParentFile)
    val ftlPathname = ftl.getPathname
    logger.debug("Reading template file "+ftlPathname)            
    val template = fmCfg.getTemplate(ftl.getName)
    func(template, ftlPathname)
  }
  
  /** Return a filename based on orig ftl pathname. And if the name has replacable
   * variable, do a lookup and replace it with the returned value.
   */
  def createDestinationFile(ftlPathname: String, destDir: File)(lookup: (String)=>String): File = {
    logger.debug("Orig ftlPathname " + ftlPathname)
    val names = Regex.findGroupsByDelimeters(ftlPathname, filenameREPrefix, filenameRESuffix, filenameREEscape)
    logger.debug("Variable names found: " + names)
    var pathname = new StringHelper(ftlPathname)
    for(name <- names){
      val variableName = filenameREPrefix+name+filenameRESuffix
      val modelValue = lookup(name)
      logger.debug("Replacing "+variableName+" with "+modelValue)
      pathname = pathname.fastReplace(variableName, modelValue)        
    }
    
    val destFile = new File(destDir, pathname.toString)
    val parentDir = destFile.getParentFile
    if(!parentDir.exists){
      logger.debug("Make dir path "+parentDir)
      parentDir.mkdirs()
    }
    destFile    
  }
}

